#include "pwext.hpp"
#include <list>
#include <stdio.h>

class data;

// [[ex03db]]
// DEBUG descr A barrier implementation that doesn't need a control thread.
class barrier 
{
	// DEBUG {
public:
	barrier(int count) :
		flip_(0), flop_(0), generation_(false), 
		count_(count)
	{ }

	// May be called only by the control thread, to
	// adjust the number of workers
	int add_workers(int add)
	{
		count_ += add; // add may be negative
		return count_;
	}

	// Workers call this method to synchronize on the
	// barrier
	void sync()
	{
		pw::lockmutex lm(flop_);
		bool gen = generation_;
		flip_.signal(1);
		while(gen == generation_)
			flop_.wait();
	}

	// Control calls this method to wait for the
	// barrier to be reached by all the workers
	void wait()
	{
		flip_.wait(count_);
	}

	// Control calls this method to release the
	// workers from the barrier
	void signal()
	{
		pw::lockmutex lm(flop_);
		generation_ = !generation_;
		flop_.broadcast();
	}

protected:
	pw::semaphore flip_; // control waits for workers
	pw::pmcond flop_; // workers wait for control
	bool generation_; // alternates between the steps
	int count_; // number of worker threads synchronized by the barrier
	// DEBUG }
public:
	// ...
	// Workers call this method to synchronize on the
	// barrier, once all of them reach the barrier,
	// the barrier gets automatically signaled. One
	// worker thread gets the result true, the rest
	// false.
	bool autosync()
	{
		pw::lockmutex lm(flop_);
		bool gen = generation_;
		bool special = (flip_.signal(1) == 1); 
		if (special) {
			// this is the first thread that reached the
			// barrier, it will be controlling this round
			// of synchronization
			{
				pw::unlockmutex ul(flop_);
				flip_.wait(count_);
			}
			generation_ = !generation_;
			flop_.broadcast();
		} else {
			while(gen == generation_)
				flop_.wait();
		}
	}
	// ...
};
// END

// [[ex03dc]]
// DEBUG descr Barrier usage with self-synchronizing worker threads.
class worker : public pw::pwthread
{
public:
	worker(data *d, barrier *b, int count) :
		data_(d), barrier_(b), count_(count)
	{ }

	virtual void *execute()
	{
		while (--count_ >= 0) {
	// DEBUG {
			fprintf(stderr, "--- worker %p exchange data %p\n", this, data_);
	// DEBUG }
			// .. exchange the data with the neighbors
			barrier_->autosync();
	// DEBUG {
			fprintf(stderr, "worker %p compute data %p\n", this, data_);
	// DEBUG }
			// ... perform the computations ...
			barrier_->autosync();
		}
	// DEBUG {
			fprintf(stderr, "--- worker %p exchange data %p\n", this, data_);
	// DEBUG }
		// .. exchange the data with the neighbors last time
		return 0;
	}

protected:
	data *data_;
	barrier *barrier_;
	int count_;
};

// the function running in the control thread,
// process a list of data in parallel
void control(std::list<data *> &dlist, int steps)
{
	int nworkers = dlist.size();
	barrier bar(nworkers);
	std::list <worker *> workers;

	// start the workers
	for (std::list<data *>::iterator it = 
			dlist.begin(); it != dlist.end(); ++it) {
		worker *w = new worker(*it, &bar, steps);
		workers.push_back(w);
		w->start();
	}

	for (std::list<worker *>::iterator it = 
			workers.begin(); it != workers.end(); ++it) {
		(*it)->join();
		delete (*it);
	}
}
// END

// DEBUG {
class data {
public:
	int x_;
};

int main()
{
	std::list<data *> dd;
	dd.push_back(new data);
	dd.push_back(new data);
	dd.push_back(new data);
	control(dd, 3);
	while(!dd.empty()) {
		delete dd.front();
		dd.pop_front();
	}
	return 0;
}
// DEBUG }
/* Sample output:

--- worker 0xe9d0d0 exchange data 0xe9d010
--- worker 0xe9d290 exchange data 0xe9d050
--- worker 0xe9d450 exchange data 0xe9d090
worker 0xe9d0d0 compute data 0xe9d010
worker 0xe9d290 compute data 0xe9d050
worker 0xe9d450 compute data 0xe9d090
--- worker 0xe9d0d0 exchange data 0xe9d010
--- worker 0xe9d290 exchange data 0xe9d050
--- worker 0xe9d450 exchange data 0xe9d090
worker 0xe9d0d0 compute data 0xe9d010
worker 0xe9d290 compute data 0xe9d050
worker 0xe9d450 compute data 0xe9d090
--- worker 0xe9d0d0 exchange data 0xe9d010
--- worker 0xe9d290 exchange data 0xe9d050
--- worker 0xe9d450 exchange data 0xe9d090
worker 0xe9d0d0 compute data 0xe9d010
worker 0xe9d290 compute data 0xe9d050
worker 0xe9d450 compute data 0xe9d090
--- worker 0xe9d0d0 exchange data 0xe9d010
--- worker 0xe9d290 exchange data 0xe9d050
--- worker 0xe9d450 exchange data 0xe9d090

*/
